﻿<%@ WebHandler Language="C#" Class="img_chk" %>

using System;
using System.Web;

using System.Drawing;
using System.Drawing.Imaging;
using System.Web.SessionState;

public class img_chk : IHttpHandler, IRequiresSessionState
{
    
    public void ProcessRequest (HttpContext context) {
        //context.Response.ContentType = "text/plain";
        //context.Response.Write("Hello World");
        string strKey = "";
        byte[] data = GenerateVerifyImage(4, ref strKey);
        context.Session["CHK_CODE"] = strKey;

        //Response.ContentType = "image/vnd.wap.wbmp";
        context.Response.ContentType = "image/png";

        //Response.OutputStream.Write(data, 0, data.Length);
        context.Response.BinaryWrite(data);
        context.Response.End();
    }

    private const double PI = 3.1415926535897932384626433832795;
    private const double PI2 = 6.283185307179586476925286766559;

    private byte[] GenerateVerifyImage(int nLen, ref string strKey)
    {
        /// <summary>
        /// 生成图片验证码
        /// </summary>
        /// <param name="nLen">验证码的长度</param>
        /// <param name="strKey">输出参数，验证码的内容</param>
        /// <returns>图片字节流</returns>
        int nBmpWidth = 13 * nLen + 5;
        int nBmpHeight = 25;
        System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(nBmpWidth, nBmpHeight);
        System.Random rd = new Random((int)System.DateTime.Now.Ticks);
        int i, j;
        string strResult = "";

        //  填充位图背景
        System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(bmp);
        graph.FillRectangle(new SolidBrush(System.Drawing.Color.FromArgb(220, 220, 220))
         , 0
         , 0
         , nBmpWidth
         , nBmpHeight);


        //  绘制干扰线条:每个字符一条干扰线
        int nLines = nLen;
        int x1, y1, x2, y2;
        System.Drawing.Pen pen;// = new System.Drawing.Pen(System.Drawing.Color.FromArgb(50, 50, 205), 2);
        for (int a = 0; a < nLines; a++)
        {
            pen = new System.Drawing.Pen(System.Drawing.Color.FromArgb(0, 0, 250), rd.Next(1, 3));
            //x1 = rd.Next(nBmpWidth);
            x1 = rd.Next(a * 13, a * 13 + 5);
            y1 = rd.Next(nBmpHeight);
            //x2 = x1 + rd.Next(-1 * nBmpWidth, nBmpWidth);//rd.Next(-30,30);
            x2 = x1 + 13 + rd.Next(5);
            //y2 = y1 + rd.Next(-1 * nBmpHeight, nBmpHeight);//rd.Next(-30,30);
            y2 = rd.Next(nBmpHeight);
            graph.DrawLine(pen, x1, y1, x2, y2);
        }

        // 扭曲图片. （经OCR识别测试，扭曲的作用远不如干扰线作用，而且影响人的识别率，所以仅扭曲干扰线试图增加识别难度！如性能有问题可考虑取消此步骤。）
        System.Drawing.Bitmap bmp2 = new System.Drawing.Bitmap(nBmpWidth, nBmpHeight);
        graph = System.Drawing.Graphics.FromImage(bmp2);
        graph.FillRectangle(new SolidBrush(System.Drawing.Color.FromArgb(220, 220, 220))
         , 0
         , 0
         , nBmpWidth
         , nBmpHeight);
        int amplitude, phase;
        amplitude = rd.Next(3, 6);//幅度:字符一般3左右
        phase = rd.Next(0, 7);//初始相位：0-2*PI
        for (i = 0; i < bmp.Width; i++)
        {
            for (j = 0; j < bmp.Height; j++)
            {
                double dx = 0;
                dx = (PI2 * (double)j) / bmp.Height;
                dx += phase;
                double dy = Math.Sin(dx);
                // 取得当前点的颜色
                int nOldX = 0, nOldY = 0;
                nOldX = i + (int)(dy * amplitude);
                nOldY = j;
                System.Drawing.Color color = bmp.GetPixel(i, j);
                if (nOldX >= 0 && nOldX < bmp2.Width
                 && nOldY >= 0 && nOldY < bmp2.Height)
                {
                    bmp2.SetPixel(nOldX, nOldY, color);
                }
            }
        }

        // 循环取得字符，并绘制
        // 采用的字符集，可以随即拓展，并可以控制字符出现的几率
        string strCode = "abcdefghijklmnopqrstuvwxyz";// "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; "abcdefghijklmnopqrstuvwxyz" "0123456789"
        for (i = 0; i < nLen; i++)
        {
            int x = (i * 13 + rd.Next(3));
            int y = rd.Next(4) + 1;

            // 确定字体:Courier New细节更多，应该比Arial更难识别
            System.Drawing.Font font = new System.Drawing.Font("Courier New",
             rd.Next(12, 16),
             System.Drawing.FontStyle.Bold);
            char c = strCode[rd.Next(strCode.Length)];  // 随机获取字符
            strResult += c.ToString();

            // 绘制字符
            graph.DrawString(c.ToString(),
             font,
             new SolidBrush(System.Drawing.Color.FromArgb(0, 0, 250)),
             x,
             y);
        }



        // 4. 输出字节流
        System.IO.MemoryStream bstream = new System.IO.MemoryStream();
        bmp2.Save(bstream, System.Drawing.Imaging.ImageFormat.Png);
        bmp2.Dispose();
        graph.Dispose();
        strKey = strResult;
        byte[] byteReturn = bstream.ToArray();
        bstream.Close();
        return byteReturn;

        // 4. 输出字节流WBMP
        //byte[] byteReturn = GetWbmp(bmp, nBmpWidth, nBmpHeight);
        //return byteReturn;

    }

    private byte[] GetWbmp(Bitmap bmpImage, int intWX, int intWY)
    {
        //应用：GetWbmp(bitmap文件,图像宽度,图像高度)，所获取的byte数据即为wbmp流，具体应用如下：
        //byte[]byteWbmp=GetWbmp(bitImage,intX,intY);
        //...
        //Response.ContentType="image/vnd.wap.wbmp";
        //Response.BinaryWrite(byteWbmp);

        //WBMP的格式如下(例如Width=16,Height=15的一个白色的画面)： 
        //0x00,0x00,0x10,0x0f,0xff,0xff......,0xff。 
        //头两个字节为0，现在还不清楚它的用途。 
        //第三个字节为Width,第四个字节为Height,后面的是数据。 
        //数据有以下特点：用一个Bit对应一个Pixel。0表示黑色，1表示白色。
        //高位开始到底位结束对应着从左到右，数据从低地址到高地址对应着从上到下。
        //一个字节可以表示8个像素，不足的部分可以用0补齐。

        int intWXl = (intWX + 7) / 8;
        int intWImgl = intWXl * intWY + 4;
        byte[] byteWImg = new byte[intWImgl];
        byteWImg[2] = (byte)intWX;
        byteWImg[3] = (byte)intWY;
        byte byteTmp = 0;
        int k = 4;//wbmp文件数据指针 
        int l = 0;//位->字节计数器
        int m = 7;//移位器：从高位到低位
        for (int i = 0; i < intWY; i++)
        {
            for (int j = 0; j < intWX; j++)
            {
                Color c = bmpImage.GetPixel(j, i);
                //if (c.R == 255) 
                if ((c.R * 0.299) + (c.G * 0.587) + (c.B * 0.114) >= 128)//判断黑白！
                    byteTmp = (byte)(byteTmp + Math.Pow(2, m));
                l++;
                m--;
                if (l > 7 || j == intWX - 1)
                {
                    byteWImg[k] = byteTmp;
                    byteTmp = 0;
                    k++;
                    l = 0;
                    m = 7;
                }

            }
        }
        return byteWImg;
    }
    
    public bool IsReusable {
        get {
            return false;
        }
    }

}